home *** CD-ROM | disk | FTP | other *** search
- (provide "graphics2")
- (require "graphics")
-
- ;;;;
- ;;;;
- ;;;; Scatmat Object Prototype
- ;;;;
- ;;;;
-
- (send scatmat-proto :title "Scatterplot Matrix")
- (send scatmat-proto :menu-title "Scatmat")
- (send scatmat-proto :fixed-aspect t)
- (send scatmat-proto :menu-template (send graph-proto :menu-template))
-
- ;;;;
- ;;;;
- ;;;; Spinner Object Prototype
- ;;;;
- ;;;;
-
- (send spin-proto :title "Spinning Plot")
- (send spin-proto :menu-title "Spinner")
- (send spin-proto :fixed-aspect t)
- (send spin-proto :menu-template (append (send graph-proto :menu-template)
- '(dash faster slower cuing axes)))
- (send spin-proto :variable-labels '("X" "Y" "Z"))
- (send spin-proto :black-on-white nil)
- (send spin-proto :depth-cuing t)
- (send spin-proto :showing-axes t)
- (send spin-proto :scale-type 'variable)
-
- (defmeth spin-proto :isnew (&rest args)
- (apply #'call-next-method args)
- (send self :add-overlay (send spin-control-overlay-proto :new)))
-
- (defmeth spin-proto :adjust-to-data (&key (draw t))
- (call-next-method :draw nil)
- (when (null (send self :scale-type))
- (let* ((vars (send self :num-variables))
- (ranges (send self :range (iseq 0 (- vars 1))))
- (radius (* (sqrt vars)
- (max (- (min ranges)) (max ranges)))))
- (send self :center (iseq vars) 0 :draw nil)
- (send self :range (iseq vars) (- radius) radius :draw nil)))
- (when draw
- (send self :resize)
- (send self :redraw)))
-
- (defmeth spin-proto :rotation-type (&optional (new nil set))
- (if set (setf (slot-value 'rotation-type) new))
- (slot-value 'rotation-type))
-
- (defmeth spin-proto :make-menu-item (item)
- (if (symbolp item)
- (case item
- (faster (send spin-speed-item-proto :new self 1.5))
- (slower (send spin-speed-item-proto :new self (/ 2 3)))
- (cuing (send graph-item-proto :new "Depth Cuing" self
- :depth-cuing :depth-cuing :toggle t :redraw t))
- (axes (send graph-item-proto :new "Show Axes" self
- :showing-axes :showing-axes :toggle t :redraw t))
- (t (call-next-method item)))
- item))
-
- (defmeth spin-proto :add-surface (x y z &key (draw t) (type 'solid) (spline 3))
- "Args: (x y z &key (draw t) (type 'solid) (spline 3))
- Adds a grid surface using sequences X, Y with values in the matrix Z.
- Z should be (length X) by (length Y)."
- (let ((z (row-list z)))
- (mapcar #'(lambda (u z)
- (let* ((yz (if spline
- (spline y z :xvals (* spline (length y)))
- (list y z)))
- (y (first yz))
- (z (second yz)))
- (send self
- :add-lines
- (list (repeat u (length y)) y z)
- :draw nil :type type)))
- x z))
- (let ((z (column-list z)))
- (mapcar #'(lambda (u z)
- (let* ((xz (if spline
- (spline x z :xvals (* spline (length x)))
- (list x z)))
- (x (first xz))
- (z (second xz)))
- (send self
- :add-lines
- (list x (repeat u (length x)) z)
- :draw nil :type type)))
- y z))
- (if draw (send self :redraw))
- nil)
-
- (defmeth spin-proto :add-function (f xmin xmax ymin ymax &rest args &key (num-points 6))
- "Args: (f xmin xmax ymin ymax &rest args &key (num-points 6))
- Adds surface of function F over a NUM-POINTS by NUM-POINTS grid on the
- rectangle [xmin, xmax] x [ymin, ymax]. Passes other keywords to
- :add-surface method."
- (let* ((x (rseq xmin xmax num-points))
- (y (rseq ymin ymax num-points))
- (z (outer-product x y f)))
- (apply #'send self :add-surface x y z args)))
-
- (defmeth spin-proto :abcplane (a b c)
- "Message args: (a b c)
- Adds the graph of the plane A + B x + Cy to the plot."
- (let ((xlimits (send self :range 0))
- (ylimits (send self :range 1)))
- (send self :add-function #'(lambda (x y) (+ a (* b x) (* c y)))
- (- (mean xlimits) (/ (abs (apply #'- xlimits)) (* 2 (sqrt 3))))
- (+ (mean xlimits) (/ (abs (apply #'- xlimits)) (* 2 (sqrt 3))))
- (- (mean ylimits) (/ (abs (apply #'- ylimits)) (* 2 (sqrt 3))))
- (+ (mean ylimits) (/ (abs (apply #'- ylimits)) (* 2 (sqrt 3))))
- :spline nil)))
-
- ;;
- ;; Spinner control overlay
- ;;
-
- (defproto spin-control-overlay-proto
- '(top lefts gap side ascent box-top text-base)
- ()
- graph-overlay-proto)
-
- (defmeth spin-control-overlay-proto :isnew ()
- (setf (slot-value 'gap) 5)
- (setf (slot-value 'side) 10)
- (setf (slot-value 'ascent) (send graph-proto :text-ascent))
- (let ((w1 (send graph-proto :text-width "Pitch"))
- (w2 (send graph-proto :text-width "Roll"))
- (w3 (send graph-proto :text-width "Yaw"))
- (gap (slot-value 'gap))
- (side (slot-value 'side)))
- (setf (slot-value 'lefts)
- (list (* 2 gap)
- (+ (* 3 gap) side)
- (+ (* 6 gap) (* 2 side) w1)
- (+ (* 7 gap) (* 3 side) w1)
- (+ (* 11 gap) (* 4 side) w1 w2)
- (+ (* 12 gap) (* 5 side) w1 w2)))))
-
- (defmeth spin-control-overlay-proto :resize ()
- (let* ((graph (send self :graph))
- (height (send graph :canvas-height))
- (bottom-margin (fourth (send graph :margin)))
- (top (+ (- height bottom-margin) 1))
- (gap (slot-value 'gap))
- (side (slot-value 'side))
- (ascent (send graph :text-ascent))
- (text-base (+ top gap (max side ascent)))
- (box-top (- text-base side)))
- (setf (slot-value 'top) top)
- (setf (slot-value 'text-base) text-base)
- (setf (slot-value 'box-top) box-top)))
-
- (defmeth spin-control-overlay-proto :redraw ()
- (let ((graph (slot-value 'graph))
- (top (slot-value 'top))
- (lefts (slot-value 'lefts))
- (gap (slot-value 'gap))
- (side (slot-value 'side))
- (text-base (slot-value 'text-base))
- (box-top (slot-value 'box-top)))
- (send graph :draw-line 0 top (send graph :canvas-width) top)
- (mapcar #'(lambda (x) (send graph :frame-rect x box-top side side))
- lefts)
- (mapcar #'(lambda (s x y) (send graph :draw-string s x y))
- '("Pitch" "Roll" "Yaw")
- (+ (select lefts '(1 3 5)) gap side)
- (repeat text-base 3))))
-
- (defmeth spin-control-overlay-proto :do-click (x y m1 m2)
- (let ((graph (slot-value 'graph))
- (top (slot-value 'top))
- (lefts (slot-value 'lefts))
- (gap (slot-value 'gap))
- (side (slot-value 'side))
- (text-base (slot-value 'text-base))
- (box-top (slot-value 'box-top)))
- (when (< top y)
- (send graph :idle-on nil)
- (if (< box-top y text-base)
- (let ((i (car (which (< lefts x (+ lefts side)))))
- (angle (abs (send graph :angle))))
- (when i
- (send graph :rotation-type
- (select '(pitching rolling yawing)
- (floor (/ i 2))))
- (send graph :angle (if (oddp i) angle (- angle)))
- (send graph :while-button-down
- #'(lambda (x y) (send graph :rotate))
- nil)
- (send graph :idle-on m1))))
- t)))
-
-
- ;;
- ;; Spinner Menu Items
- ;;
-
- ;; SPIN-SPEED-ITEM-PROTO. multiply speed by fixed number to speed up or slow down.
- (defproto spin-speed-item-proto '(graph mult) () menu-item-proto)
-
- (defmeth spin-speed-item-proto :isnew (v m)
- (setf (slot-value 'graph) v)
- (setf (slot-value 'mult) m)
- (call-next-method (if (> 1 m) "Slower" "Faster"))
- (send self :key (if (> 1 m) #\S #\F)))
-
- (defmeth spin-speed-item-proto :do-action ()
- (send (slot-value 'graph) :angle (* (slot-value 'mult) (send (slot-value 'graph) :angle))))
-
-
- ;;;;
- ;;;;
- ;;;; Spinner Functions
- ;;;;
- ;;;;
-
- (defun spin-function (f xmin xmax ymin ymax &rest args)
- "Args: (f xmin xmax ymin ymax &key (num-points 6) (spline 3))
- Rotatable plot of function F of two real variables over the range
- between [xmin, xmax] x [ymin, ymax]. The function is evaluated at
- NUM-POINTS points. If SPLINE is not NIL a spline is fit at
- (* SPLINE NUMPOINTS) points."
- (let ((plot (apply #'send spin-proto :new 3 :show nil args)))
- (apply #'send plot :add-function f xmin xmax ymin ymax :draw nil args)
- (send plot :adjust-to-data :draw nil)
- (send plot :new-menu)
- (send plot :showing-axes nil)
- (send plot :rotate-2 0 1 (/ pi 3) :draw nil)
- (send plot :rotate-2 1 2 (- (/ pi 3)) :draw nil)
- (send plot :show-window)
- plot))
-
- ;;;;
- ;;;;
- ;;;; Name List Object Prototype
- ;;;;
- ;;;;
-
- (send name-list-proto :title "Name List")
- (send name-list-proto :menu-title "List")
- (send name-list-proto :menu-template '(link mouse dash erase-selection
- focus-on-selection show-all
- #+color color
- selection dash options
- #-macintosh save-image))
-
- ;;;;
- ;;;;
- ;;;; Histogram Object Prototype
- ;;;;
- ;;;;
-
- (send histogram-proto :title "Histogram")
- (send histogram-proto :menu-title "Histogram")
- (send histogram-proto :fixed-aspect nil)
- (send histogram-proto :size 250 125)
- (send histogram-proto :menu-template '(link mouse resize-brush dash
- erase-selection
- focus-on-selection show-all
- #+color color
- selection dash
- #-small-machine slicer
- rescale
- #-small-machine options
- #-macintosh save-image
- dash change-bins))
-
- (defmeth histogram-proto :make-menu-item (item)
- (if (symbolp item)
- (case item
- (change-bins (send change-hist-bins-item-proto :new self))
- (t (call-next-method item)))
- item))
-
- (defmeth histogram-proto :drag-point (x y &key (draw t))
- (let ((p (call-next-method x y :draw nil)))
- (if p (send self :resize))
- (if (and p draw) (send self :redraw))
- p))
-
- ;;
- ;; Histogram Menu Items
- ;;
-
- ;; CHANGE-HIST-BINS-ITEM-PROTO. Opens new integer dialog.
- (defproto change-hist-bins-item-proto '(graph) () menu-item-proto)
-
- (defmeth change-hist-bins-item-proto :isnew (h)
- (setf (slot-value 'graph) h)
- (call-next-method "Change Bins"))
-
- (defmeth change-hist-bins-item-proto :do-action ()
- (let ((bins (get-new-integer "Number of bins"
- 2
- 30
- (send (slot-value 'graph) :num-bins))))
- (when bins
- (send (slot-value 'graph) :num-bins bins)
- (send (slot-value 'graph) :redraw))))
-
- ;;;;
- ;;;;
- ;;;; Scatterplot Object Prototype
- ;;;;
- ;;;;
-
- (send scatterplot-proto :title "Plot")
- (send scatterplot-proto :menu-title "Plot")
- (send scatterplot-proto :fixed-aspect nil)
- (send scatterplot-proto :menu-template (send graph-proto :menu-template))
-
- (defmeth graph-proto :add-function (f xmin xmax &rest args &key (num-points 50))
- "Message args: (f xmin xmax &key (num-points 50)
- Adds plot of function F of one real variable over the range between xmin
- and xmax to the plot. The function is evaluated at NUM-POINTS points."
- (unless (= 2 (send self :num-variables)) (error "only works for 2D plots"))
- (let* ((x (rseq xmin xmax num-points))
- (y (mapcar f x)))
- (apply #'send self :add-lines (list x y) args)))
-
- (defmeth graph-proto :abline (a b)
- "Message args: (a b)
- Adds the graph of the line A + B x to the plot."
- (let ((limits (send self :range 0)))
- (send self :add-function #'(lambda (x) (+ a (* b x)))
- (car limits)
- (cadr limits))))
-
- (defmeth graph-proto :plotline (a b c d draw)
- (send self :add-lines (list a c) (list b d) :draw draw))
-
- ;;;;
- ;;;;
- ;;;; Basic 2D Plotting Functions
- ;;;;
- ;;;;
-
- (defun plot-function (f xmin xmax &key (num-points 50) (type 'solid) labels)
- "Args: (f xmin xmax &optional (num-points 50) labels)
- Plots function F of one real variable over the range between xmin and xmax.
- The function is evaluated at NUM-POINTS points. LABELS is a list of axis
- labels."
- (let* ((x (rseq xmin xmax num-points))
- (y (mapcar f x)))
- (plot-lines x y :type type :variable-labels labels)))
-
- ;;;;
- ;;;;
- ;;;; Boxplot Functions
- ;;;;
- ;;;;
-
- (defmeth scatterplot-proto :add-boxplot (y &key (x 1.0) (width 1.0) (draw t))
- (unless (= 2 (send self :num-variables)) (error "only works for 2D plots"))
- (let* ((half-box (* 0.4 width))
- (half-foot (* 0.1 width))
- (fiv (fivnum y))
- (low (select fiv 0))
- (q1 (select fiv 1))
- (med (select fiv 2))
- (q3 (select fiv 3))
- (high (select fiv 4)))
- (send self :plotline (- x half-foot) low (+ x half-foot) low nil)
- (send self :plotline (- x half-foot) high (+ x half-foot) high nil)
- (send self :plotline x low x q1 nil)
- (send self :plotline x q3 x high nil)
- (send self :plotline (- x half-box) q1 (+ x half-box) q1 nil)
- (send self :plotline (- x half-box) med (+ x half-box) med nil)
- (send self :plotline (- x half-box) q3 (+ x half-box) q3 nil)
- (send self :plotline (- x half-box) q1 (- x half-box) q3 nil)
- (send self :plotline (+ x half-box) q1 (+ x half-box) q3 nil)))
-
- (defun boxplot (data &key (title "Box Plot"))
- "Args: (data &key (title \"Box Plot\"))
- DATA is a sequence, a list of sequences or a matrix. Makes a boxplot of the
- sequence or a parallel box plot of the sequences in the list or the columns
- of the matrix."
- (let ((p (send scatterplot-proto :new 2 :title title :show nil)))
- (setq data
- (cond ((matrixp data) (column-list data))
- ((or (not (listp data)) (numberp (car data))) (list data))
- (t data)))
- (let ((range (get-nice-range (min data) (max data) 4)))
- (send p :range 1 (nth 0 range) (nth 1 range))
- (send p :y-axis t nil (nth 2 range)))
- (send p :range 0 0 (1+ (length data)))
- (dotimes (i (length data))
- (send p :add-boxplot (nth i data) :x (1+ i)))
- (send p :show-window)
- p))
-
- (defun boxplot-x (x data &key (title "Box Plot"))
- "Args: (x data &key (title \"Box Plot\"))
- DATA is a list of sequences or a matrix. X is a sequence with as many
- elements as DATA has elements or columns. Makes a parallel box plot
- of the sequences in the list or the columns of the matrix vs X."
- (let ((p (send scatterplot-proto :new 2 :title title :show nil)))
- (setq data
- (cond ((matrixp data) (column-list data))
- ((or (not (listp data)) (numberp (car data))) (list data))
- (t data)))
- (let ((range (get-nice-range (min data) (max data) 4)))
- (send p :range 1 (nth 0 range) (nth 1 range))
- (send p :y-axis t nil (nth 2 range)))
- (setq x (coerce x 'list))
- (if (/= (length x) (length data)) (error "argument lengths do not match"))
- (let* ((width (min (difference x)))
- (range (get-nice-range (- (min x) width) (+ (max x) width) 4)))
- (send p :range 0 (nth 0 range) (nth 1 range))
- (send p :x-axis t nil (nth 2 range))
- (dotimes (i (length data))
- (send p :add-boxplot (nth i data) :width width :x (nth i x))))
- (send p :show-window)
- p))
-
- ;;;;
- ;;;;
- ;;;; Quantile and Probability Plot Functions
- ;;;;
- ;;;;
-
- (defun quantile-plot (x &key (quantile-function #'normal-quant)
- (title "Quantile Plot") point-labels)
- "Args: (data &key (quantile-function #'normal-quant) (title \"Quantile Plot\") point-labels)"
- (plot-points (funcall quantile-function
- (/ (1+ (rank x)) (1+ (length x))))
- x
- :title title
- :axis-labels
- '("Theoretical Quantiles" "Observed Quantiles")
- :point-labels point-labels))
-
- (defun probability-plot (x &key (distribution-function #'normal-cdf)
- (title "Probability Plot") point-labels)
- "Args: (data &key (distribution-function #'normal-cdf) (title \"Probability Plot\") point-labels)"
- (let ((p (plot-points (/ (1+ (rank x)) (1+ (length x)))
- (funcall distribution-function x)
- :title title
- :variable-labels '("Theoretical CDF" "Observed CDF")
- :point-labels point-labels)))
- (send p :x-axis t t 5)
- (send p :y-axis t t 5)
- p))
-
- ;;;;
- ;;;;
- ;;;; Contour Plotting Methods and Functions
- ;;;;
- ;;;;
-
- (defmeth scatterplot-proto :add-surface-contour (x y z v &key (draw t))
- (let ((c (surface-contour x y z v)))
- (dolist (x (split-list c 2))
- (send self :add-lines (transpose x) :draw nil)))
- (if draw (send self :redraw-content)))
-
- (defmeth scatterplot-proto :add-surface-contours
- (x y z &optional v &key (draw t))
- (let ((v (if v
- (if (numberp v) (list v v) (coerce v 'list))
- (let ((min (min z))
- (max (max z)))
- (+ min (* (- max min) '(.2 .4 .6 .8)))))))
- (dolist (v v) (send self :add-surface-contour x y z v :draw nil))
- (if draw (send self :redraw-content))))
-
- (defmeth scatterplot-proto :add-function-contours
- (f xmin xmax ymin ymax &optional v &key (num-points 6) (draw t))
- (let* ((x (coerce (rseq xmin xmax num-points) 'vector))
- (y (coerce (rseq ymin ymax num-points) 'vector))
- (z (outer-product x y f)))
- (send self :add-surface-contours x y z v :draw draw)))
-
- (defun contour-function (f xmin xmax ymin ymax &rest args
- &key levels (num-points 6))
- "Args: (f xmin xmax ymin ymax &key levels (num-points 6))
- Contour plot of function F of two real variables over the range
- between [xmin, xmax] x [ymin, ymax]. The function is evaluated at
- NUM-POINTS points."
- (let ((plot (apply #'send scatterplot-proto :new 2 :show nil args)))
- (send plot :add-function-contours f xmin xmax ymin ymax
- levels :num-points num-points :draw nil)
- (send plot :adjust-to-data :draw nil)
- (send plot :new-menu)
- (send plot :show-window)
- plot))
-
- #-small-machine (require "graphics3")
-